package com.newlink.building.library_rino

import android.content.Context
import android.media.AudioManager
import android.os.Environment
import android.util.Log
import android.widget.LinearLayout
import com.newlink.building.library_rino.utils.RinoAudioUtils
import com.smart.rinoiot.common_sdk.listener.BaseRequestListener
import com.smart.rinoiot.common_sdk.utils.LgUtils
import com.smart.rinoiot.common_sdk.utils.ToastUtil
import com.smart.rinoiot.device_sdk.bean.device.AgoraUserTokenVO
import com.smart.rinoiot.panel_sdk.ipc.agora.RinoEventListener
import com.smart.rinoiot.panel_sdk.ipc.agora.RinoIPCEventEmitter
import com.smart.rinoiot.panel_sdk.rinoIPCSDK.RinoIPCSDK
import com.smart.rinoiot.panel_sdk.rinoIPCSDK.RinoRemotePlayer
import com.smart.rinoiot.panel_sdk.rinoIPCSDK.api.RinoIPCBusiness
import com.newlink.building.common_base.base.BaseApplication
import com.newlink.building.common_base.constant.Base_Constant
import com.newlink.building.common_base.event.CallCreateActionBean
import com.newlink.building.common_base.event.MessageEvent
import com.newlink.building.common_base.event.TYPE_CALL_UPDATE_TOKEN_REQUEST
import com.newlink.building.common_base.event.TokenVo
import com.newlink.building.common_base.ext.showToast
import com.newlink.building.common_base.utils.NLog
import com.newlink.building.common_base.utils.LogHandlerThread
import com.newlink.building.common_base.utils.NetWorkUtil
import com.newlink.building.common_base.utils.Base_Preference
import io.agora.base.VideoFrame
import io.agora.rtc2.video.VideoEncoderConfiguration.VideoDimensions
import org.greenrobot.eventbus.EventBus
import java.io.File
import java.util.Collections


/**
 * @Author: Jake
 * @Date: 2024-04-11
 * @Description:
 */
class ConnectionController private constructor(private val context: Context) : RinoEventListener {

    companion object {
        private const val TAG: String = "NetworkHandler_core"

        // 设备端媒体流输出端口 1003 , 输入端口1004
        const val DEVICE_REMOTE_ID: Int = 200000007
//        const val DEVICE_REMOTE_ID: Int = 3

        //connectModel 连接模式(1-预连接,3-直播推流,默认直播推流)
        const val DEVICE_CONNECT_MODEL: Int = 3

        //动态设置编码
        var dynamicAudioCodec: Int = 8

        @Volatile
        private var instance: ConnectionController? = null

        private var isMicEnabled: Boolean = false

        private var isSpeakerEnable: Boolean = false

        private var isMuteAudio: Boolean = false

        private var hasJoinChannelJob: Boolean = false

        private var rinoRemotePlayer: RinoRemotePlayer? = null

        fun getInstance(context: Context): ConnectionController {
            return instance ?: synchronized(this) {
                instance ?: ConnectionController(context.applicationContext).also { instance = it }
            }
        }
    }

    //    private var weakReference: WeakReference<Context>? = null
    private var mRinoIPCEventEmitter: RinoIPCEventEmitter? = null
    private var agoraUserTokenVO: AgoraUserTokenVO? = null
    private var remoteChannelName: String = ""
    private var remoteUid: Int = 0
    private var isMonitor = false
    private var agoraAppId = ""

    //    private var mEventBus: EventBus = EventBus.getDefault()
    protected var agoraId: Int by Base_Preference(Base_Constant.AGORA_ID_KEY, 0)
    private var callStartBasicTimeStamp: Long = 0L

    init {
//        mEventBus.register(this)
        mRinoIPCEventEmitter = RinoIPCEventEmitter().also {
            it.addListener(this)
        }
    }

    private var rinoEventListeners: MutableList<com.newlink.building.library_rino.listener.RinoEventListener> =
        mutableListOf()
    private var mPlayerContainer: LinearLayout? = null

    fun addRinoEventListener(listener: com.newlink.building.library_rino.listener.RinoEventListener) {
        rinoEventListeners.add(listener)
    }

    fun removeRinoEventListener(listener: com.newlink.building.library_rino.listener.RinoEventListener) {
        rinoEventListeners.remove(listener)
    }

    fun clearRinoEventListeners() {
        rinoEventListeners.clear()
    }


    fun setAgoraUserToken(token: AgoraUserTokenVO) {
        loged("[Rino] 设置token : ${token.rtcToken.rtcToken}  ")
        agoraUserTokenVO = token
    }

    fun getAgoraUserToken(): AgoraUserTokenVO? = this.agoraUserTokenVO

    fun clearAgoraUserToken() {
        loged("[Rino] clearAgoraUserToken  ")
        agoraUserTokenVO = null
    }

    fun isMonitorState(): Boolean {
        return isMonitor
    }

    fun setMonitorState(isMonitor: Boolean) {
        this.isMonitor = isMonitor
    }

    fun setAgoraAppId(appId: String) {
        this.agoraAppId = appId
    }

    fun getChannelName(): String? = agoraUserTokenVO?.rtcToken?.channelName

    fun getRemoteChannelName() = remoteChannelName

    fun setRemoteChannelName(channelName: String) {
        loged("setRemoteChannelName channelName:$channelName")
        remoteChannelName = channelName
    }

    fun setRemoteUid(uid: Int) {
        loged("setRemoteUid uid:$uid")
        remoteUid = uid
    }

    fun setAudioCodec(codec: Int) {
        dynamicAudioCodec = codec
    }

    fun getRemoteUid() = remoteUid

    fun setBasicTimeStamp(basicTimeStamp: Long) {
        loged("setBasicTimeStamp $basicTimeStamp")
        callStartBasicTimeStamp = basicTimeStamp
    }

    fun clearCacheAtBeginning() {
        loged("clearCacheAtBeginning...")
        //发起INVITE事件清一下ChannelName,防止偶现的Call.State.Released时未清除情况,家庭成员呼叫时需要特殊处理
        setRemoteChannelName("")
        //极低概率设备不回Bye 200 ACK,确保下一次通话清理Rino Token
        clearAgoraUserToken()
        //开始清除加入房间的标记
        cleanJoinChanneJob()
        //清除Event事件监听
        clearRinoEventListeners()
    }

    fun convert2AgoraUserToken(tokenVO: TokenVo?, rinoId: Int, appId: String): AgoraUserTokenVO? {
        loged("Rconvert2AgoraUserToken tokenVO:${tokenVO?.rtcToken} rinoId:$rinoId appId : $appId")
        tokenVO?.let { token ->
            val agoraUserTokenVO = AgoraUserTokenVO().apply {
                agoraAppId = appId
                rtcToken = tokenVO.rtcToken
                rtmToken = tokenVO.rtmToken
                userId = "$rinoId"
            }
            setAgoraUserToken(agoraUserTokenVO)
            return agoraUserTokenVO
        }
        return null
    }

    fun initRinoSDK(appId: String) {
        loged("RinoIPCSDK initRinoSDK hasInit:${RinoIPCSDK.hasInit} appId : $appId")
        if (!RinoIPCSDK.hasInit) { //以初始化
            RinoIPCSDK.init(appId, mRinoIPCEventEmitter, context)
            loged("RinoIPCSDK 初始化完成")
        }
    }

    fun joinChannel(
        playerContainer: LinearLayout,
        localUid: Int,
        targetAId: Int = DEVICE_REMOTE_ID,
        isSpeakerOn: Boolean = true
    ) {
        saveLog("joinChannel  cn:${agoraUserTokenVO?.rtcToken?.channelName} uid:$remoteUid job:$hasJoinChannelJob")
        loged("joinChannel ..................... channelName:${agoraUserTokenVO?.rtcToken?.channelName} remoteUid:$remoteUid hasJoinChannelJob:$hasJoinChannelJob")
        if (mPlayerContainer != playerContainer) {
            loged("playerContainer is changed clear join channel job!")
            hasJoinChannelJob = false
        }
        if (hasJoinChannelJob) return
        agoraUserTokenVO?.let { token ->
            initRinoSDK(token.agoraAppId)
            hasJoinChannelJob = true
            NetWorkUtil.ping(Base_Constant.BASE_URL)
            initRinoPlayer(playerContainer, remoteUid)
            RinoAudioUtils.choiceAudioModel(context, isSpeakerOn)
//            RinoIPCBusiness.testNotifyDeviceJoinLiveChannel(
//                token.rtcToken.channelName,
//                DEVICE_CONNECT_MODEL,
//                token,
//                object : BaseRequestListener<Any?>() {
//                    override fun onResult(result: Any?) {
//                        super.onResult(result)
//                        loged("加入房间完成")
//                    }
//
//                    override fun onError(error: String, msg: String) {
//                        super.onError(error, msg)
//                        loged("加入房间失败")
//                    }
//                })

        }
//        startPushAudioToChannel(localUid)
////        startLocalAudio()
////        startVideoTalk()
//        setEnableSpeakerphone(false)
    }

    private fun initRinoPlayer(
        container: LinearLayout,
        remoteUid: Int = DEVICE_REMOTE_ID,
    ) {
//        weakReference = WeakReference(container.context)
        loged("[jake]` initRinoPlayer remoteUid:$remoteUid  currentUid:" + agoraUserTokenVO?.rtcToken?.uid)
        agoraUserTokenVO?.let { token ->
            rinoRemotePlayer = RinoRemotePlayer(BaseApplication.context, remoteUid, false)
            rinoRemotePlayer?.token = token
            rinoRemotePlayer?.setMuteAudio(isMuteAudio)
            rinoRemotePlayer?.setAutoLeaveChannelOnDestroy(false)
            mPlayerContainer = container
            container.addView(
                rinoRemotePlayer, LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.MATCH_PARENT
                )
            )
        }
    }

    fun getPlayerContainer(): LinearLayout? {
        return mPlayerContainer
    }

    fun clearPlayer() {
        cleanJoinChanneJob()
    }

    fun releasePlayer(cleanJob: Boolean = true) {
        loged("releasePlayer..... cleanJob:$cleanJob")
        if (cleanJob) {
            cleanJoinChanneJob()
        }
        mPlayerContainer?.let {
            if (it.isAttachedToWindow) {
                it.removeAllViews()
            }
        }
        rinoRemotePlayer?.removeAllViews()
        rinoRemotePlayer = null
        mPlayerContainer = null
    }

    fun cleanJoinChanneJob() {
        hasJoinChannelJob = false
    }

    fun hasJoinChanneJob(): Boolean = hasJoinChannelJob

    fun onlyLeaveRinoIPCSDK() {
        loged("onlyLeaveRinoIPCSDK.....")
        val channelName = agoraUserTokenVO?.rtcToken?.channelName
        val userId = agoraUserTokenVO?.userId
        if (channelName?.isNotEmpty() == true && userId?.isNotEmpty() == true) {
            RinoIPCSDK.leaveChannel(agoraUserTokenVO?.rtcToken?.channelName, userId.toInt())
        }
    }

    fun leaveChannel() {
        loged("leaveChannel.....")
        val channelName = agoraUserTokenVO?.rtcToken?.channelName
        val userId = agoraUserTokenVO?.userId
        loged("[leave]..1...$rinoRemotePlayer")
        if (channelName?.isNotEmpty() == true && userId?.isNotEmpty() == true) {
            RinoIPCSDK.leaveChannel(agoraUserTokenVO?.rtcToken?.channelName, userId.toInt())
        }
        clearAgoraUserToken()
        remoteChannelName = ""
        remoteUid = 0
        loged("[leave]..2...$rinoRemotePlayer")
        loged("rinoRemotePlayer onDestroy.....")
        releasePlayer()
//        if (rinoRemotePlayer != null) {
//            rinoRemotePlayer = null
//        }
    }

    fun isMicEnabled(): Boolean = isMicEnabled
    fun setMicEnabled(enable: Boolean) {
        isMicEnabled = enable
    }

    fun isSpeakerEnable(): Boolean = isSpeakerEnable
    fun setSpeakerEnabled(enable: Boolean) {
        isSpeakerEnable = enable
    }

    fun isMuteAudio(): Boolean = isMuteAudio

    fun setMuteAudio(mute: Boolean) {
        loged("[event] setMuteAudio mute:$mute")
        isMuteAudio = mute
        rinoRemotePlayer?.setMuteAudio(isMuteAudio)
    }

    override fun onEvent(event: RinoIPCEventEmitter.RinoIPCEvent, context: Context) {
//        loged("[event] onEvent eventType >>>>>  ${event.eventType} ")
        when (event.eventType) {
            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onConnectionStateChanged -> {
                loged("[event] eventType:onConnectionStateChanged data:${event.data}")
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onJoinChannelSuccess,
            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onRejoinChannelSuccess,
                -> {
                saveLog("${event.eventType.name}  cn:${agoraUserTokenVO?.rtcToken?.channelName}  uid:${agoraUserTokenVO?.userId} mic:$isMicEnabled")
                loged("[event] onJoinChannelSuccess  channelName:${agoraUserTokenVO?.rtcToken?.channelName}  uid:${agoraUserTokenVO?.userId} isMicEnabled:$isMicEnabled")
                agoraUserTokenVO?.userId?.let {
                    if (isMicEnabled) {
                        startPushAudioToChannel(it.toInt())
                        //app端并不会主动推送视频流,后续将此功能移除
//                        startPushVideoToChannel(
//                            agoraUserTokenVO?.rtcToken?.channelName,
//                            it.toInt(),
//                            VideoDimensions(480, 320)
//                        )
                    } else {
                        stopPushAudioToChannel(it.toInt())
                    }
                    //此回调里需要切换到相应声道和刷新UI
                    rinoEventListeners.forEach { listener ->
                        listener.onJoinChannelSuccess()
                    }
                }
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onRemoteVideoStateChanged -> {
                rinoEventListeners.forEach { listener ->
                    listener.onRemoteVideoStateChanged()
                }
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onFirstRemoteVideoFrame -> {
                loged("[event] onFirstRemoteVideoFrame  ")
                saveLog("onFirstRemoteVideoFrame  ${event.data}")
                rinoEventListeners.forEach { listener ->
                    listener.onFirstRemoteVideoFrame()
                }
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onLeaveChannel -> {
                saveLog("onLeaveChannel")
                loged("[event] onLeaveChannel  ")
                hasJoinChannelJob = false
                rinoEventListeners.forEach { listener ->
                    listener.onLeaveChannel()
                }
                //todo 对方离开通道, 挂断电话.
//                clearAgoraUserToken()
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onRemoteAudioStateChanged -> {
                loged("[event] onRemoteAudioStateChanged  ")
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onRemoteAudioStats -> {
                loged("[event] >>onRemoteAudioStats<< : ${event.data}   ")
//                val stats = Gson().fromJson(
//                    event.data.getString("stats"),
//                    IRtcEngineEventHandler.RemoteAudioStats::class.java
//                )
//                loged("[event] remote audio stats bitrate is : " +
//                        "${stats.receivedBitrate}  " +
//                        "receivedSampleRate : ${stats.receivedSampleRate}")
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onRemoteVideoStats -> {
//                loged("[event] >>onRemoteVideoStats<< : ${event.data}   ")
                // 码流信息
                // https://doc.shengwang.cn/api-ref/rtc/android/API/class_remoteaudiostats#receivedBitrate
                /**
                 * {stats={
                 * "avSyncTimeMs":-182,
                 * "decoderOutputFrameRate":20,
                 * "delay":238,
                 * "e2eDelay":0,
                 * "frameLossRate":0,
                 * "frozenRate":0,
                 * "height":720,
                 * "mosValue":0,
                 * "packetLossRate":0,
                 * "publishDuration":17582,
                 * "receivedBitrate":1557,
                 * "rendererOutputFrameRate":21,
                 * "rxStreamType":0,
                 * "rxVideoBytes":4774020,
                 * "totalActiveTime":17582,
                 * "totalFrozenTime":0,
                 * "uid":100000300,"
                 * width":1280}}
                 *
                 */
                rinoEventListeners.forEach { listener ->
                    listener.onRemoteVideoStats()
                }
                try {
                    val dataVal = event.data
                    loged("[event] 00000000000 onRemoteVideoStats>> $dataVal ")
                    dataVal?.let {
                        val statusVal = it["status"]
                        statusVal?.let {
                            loged("[event] 00000000001 onRemoteVideoStats>> $it ")
                        } ?: {
                            loged("[event] 00000000002 onRemoteVideoStats 111111111111 ")
                        }

                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                }


//                val stats = Gson().fromJson(
//                    event.data.getString("stats"),
//                    IRtcEngineEventHandler.RemoteVideoStats::class.java
//                )
//                loged("[event] video stats bitrate is : ${stats.receivedBitrate}  ")
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onUserJoined -> {
                loged("[event] onUserJoined  ")
                val dataVal = event.data
                saveLog("onUserJoined  $dataVal")
                dataVal?.let {
                    loged("[event] onUserJoined content is : $it")
                    val uid = it["uid"] as Int
                    if (uid == remoteUid) {
                        rinoEventListeners.forEach { listener ->
                            listener.onUserJoined()
                        }
                    }
                }
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onUserOffline -> {
                loged("[event] onUserOffline  ")
                val dataVal = event.data
                saveLog("onUserOffline  $dataVal")
                dataVal?.let {
                    loged("[event] onUserOffline content is : $it")
                    val uid = it["uid"] as Int
                    if (uid == remoteUid) {
                        rinoEventListeners.forEach { listener ->
                            listener.onUserOffline()
                        }
                    }
                }
            }


            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onAudioVolumeIndication -> {
                loged("[event] onAudioVolumeIndication  ")
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onTokenPrivilegeWillExpire -> {
                loged("[event] onTokenPrivilegeWillExpire  ")
                val channelName = agoraUserTokenVO?.rtcToken?.channelName

                loged("[event] rino token 即将过期 channelName:$channelName , appId:$agoraAppId , aid : $agoraId ")
                if (!channelName.isNullOrEmpty()) {
//                    val appId = if (agoraAppId.isEmpty()) {
//                        val fileName = BaseApplication.context.packageName
//                        val sp = context.getSharedPreferences(fileName, MODE_PRIVATE)
//                        sp.getString(Constant.AGORA_App_ID_KEY, "")
//                    } else {
//                        agoraAppId
//                    }
//                    loged("[event] rino 请求 token --- $appId ")
//                    if (appId.isNullOrEmpty()) {
//                        loged("[event] rino 请求 token 失败 appId 为空")
//                        return
//                    }
                    loged("[event] rino 请求 token ---agoraId :  $agoraId ")
                    val sipMsgEvent = MessageEvent(
                        TYPE_CALL_UPDATE_TOKEN_REQUEST,
                        CallCreateActionBean(
                            channelName = channelName!!,
                            aid = agoraId
                        )
                    )
                    EventBus.getDefault().post(sipMsgEvent)
                }

//                val newToken = ///
//                loged("[event] >>>> Rino 刷新token $channelName , $userId  ")
//                if (!channelName.isNullOrEmpty() && userId != null && userId > 0) {
//                    val updateResult = updateToken(channelName, userId ?: 0, "")
//                    loged("[event] >>>> Rino 刷新token 结果: $updateResult  ")
//                } else {
//                    loged("[event] >>>> Rino 刷新token 失败 channel or userid....  ")
//                }
//                rinoRemotePlayer.setToken()
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onLocalAudioStateChanged -> {
                loged("[event] onLocalAudioStateChanged  ${event.data}")
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onSnapshotTaken -> {
                loged("[event] onSnapshotTaken 11111111111 ${event.data}")

                try {
                    val dataVal = event.data
                    loged("[event]  onSnapshotTaken :  >> $dataVal ")
                    dataVal?.let {
                        val errCode = it["errCode"] as Int
                        loged("[event] >>  onSnapshotTaken<< : $errCode  ")
                        rinoEventListeners.forEach { listener ->
                            listener.onSnapshotTaken(errCode)
                        }
                    }

                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onRenderVideoFrame -> {
                Log.d("rino_test", "[event] >>onRenderVideoFrame<< : ${event.data}")

                /*
                 {uid=100000300,
                 videoFrame=io.agora.base.VideoFrame@430dc58,
                 channelId=0c3831000066_1726193686154}
                 */
                try {
                    val dataVal = event.data
                    Log.d("rino_test", "[event] 00000000000 dataMap :  >> $dataVal")
                    dataVal?.let {
                        val videoFrame = it["videoFrame"] as VideoFrame
                        val frameTimestamp = videoFrame.timestampNs
                        Log.d(
                            "rino_test",
                            "[event] onRenderVideoFrame ---> timestamp >> : $frameTimestamp >> vs >> ${System.currentTimeMillis()} "
                        )
                    }

                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }

            RinoIPCEventEmitter.RinoIPCEventTypeEnum.onPlaybackAudioFrameBeforeMixing -> {

                Log.d("rino_test", "[event] >>onPlaybackAudioFrameBeforeMixing<< : ${event.data}")

                /*
                 {uid=100000300,
                 samplesPerChannel=441,
                 bytesPerSample=2,
                 channels=1,
                 avsync_type=0,
                 renderTimeMs=1726194025833,
                 samplesPerSec=44100,
                 buffer=java.nio.DirectByteBuffer[pos=0 lim=882 cap=882],
                 type=0,
                 channelId=0c3831000066_1726194018418}
                */
            }


            else -> {}
        }
    }

//    private fun getNewToken(channelName: String, userId: Int): String {
//        RetrofitHelper.service.getNewToken(channelName, userId)
//    }

    fun destroy() {
        clearAgoraUserToken()
        releasePlayer()
        remoteChannelName = ""
        remoteUid = 0
        Log.d(TAG, "RinoIPCSDKdestroy: ")
        RinoIPCSDK.destroy()
    }

    /**
     * 只释放RinoIPCSDK,不清除本地token参数
     */
    fun destroyRinoIPCSdk() {
        loged("[event] destroyRinoIPCSdk ")
        RinoIPCSDK.destroy()
    }

    private fun loged(content: String) {
        Log.e(TAG, "[Rino] >>>> $content")
    }

//    fun startLocalAudio() {
//        loged("startLocalAudio ")
//        agoraUserTokenVO?.let { agoraUserTokenVO ->
//            // 开启或关闭音频对讲能力
//            val enabled = RinoIPCSDK.enableLocalAudio(true)
//            if (enabled == 0) {
//                /* true:是多频道；false：不是多频道
//                 * 如果是多频道，必须设置channelId和uid 的值，单频道可以直接传空
//                 * */
//                var channelId: String? = "" //渠道名
//                var uid = -1 //uid
//                if (agoraUserTokenVO.rtcToken != null) {
//                    channelId = agoraUserTokenVO.rtcToken.channelName
//                    uid = agoraUserTokenVO.rtcToken.uid.toInt()
//                }
//                RinoIPCSDK.startLocalAudio(false, channelId, uid)
//                loged("startLocalAudio channelId:$channelId  uid:$uid userid:${agoraUserTokenVO.userId}")
//            }
//        }
//
//    }
    /**
     * 关闭音频对讲 方法调用成功;   < 0: 方法调用失败
     */
//    fun stopLocalAudio() {
//        agoraUserTokenVO?.let { agoraUserTokenVO ->
//            // 开启或关闭音频对讲能力，如果是多频道，单个用户退出频道，不需要设置 enableLocalAudio(false)；全部退出时，需要设置
//            val enabled = RinoIPCSDK.enableLocalAudio(false)
//            if (enabled == 0) {
//                /* true:是多频道；false：不是多频道
//                 * 如果是多频道，必须设置channelId和uid 的值，单频道可以直接传空
//                 * */
//                var channelId: String? = "" //渠道名
//                var uid = -1 //uid
//                if (agoraUserTokenVO.rtcToken != null) {
//                    channelId = agoraUserTokenVO.rtcToken.channelName
//                    uid = agoraUserTokenVO.rtcToken.uid.toInt()
//                }
//                RinoIPCSDK.stopLocalAudio(false, channelId, uid)
//                loged("stopLocalAudio")
//            }
//        }
//
//    }

//    fun startVideoTalk() {
//        agoraUserTokenVO?.let { tokenVO ->
//            loged("startVideoTalk ")
//            RinoIPCSDK.enableLocalVideo(true)
//            try {
//                RinoIPCSDK.startLocalVideo(
//                    false,
//                    tokenVO.rtcToken.channelName,
//                    tokenVO.rtcToken.uid.toInt()
//                )
//            } catch (e: Exception) {
//                loged("startVideoTalk error $e")
//                throw RuntimeException(e)
//            }
//        }
//
//    }

//    fun stopVideoTalk() {
//        agoraUserTokenVO?.let { tokenVO ->
//            loged("stopVideoTalk")
//            RinoIPCSDK.enableLocalVideo(true)
//            try {
//                RinoIPCSDK.stopLocalVideo(
//                    false,
//                    tokenVO.rtcToken.channelName,
//                    tokenVO.rtcToken.uid.toInt()
//                )
//            } catch (e: Exception) {
//                loged("stopVideoTalk error $e")
//                throw RuntimeException(e)
//            }
//        }
//
//    }

    /**
     * 切换前置/后置摄像头
     * 该方法必须在摄像头成功开启后调用，即 SDK 触发 onLocalVideoStateChanged 回调，返回本地视频状态为 LOCAL_VIDEO_STREAM_STATE_CAPTURING (1) 后。
     *
     * @return 0: 方法调用成功;   < 0: 方法调用失败
     */
    fun switchCamera(): Int {
        return RinoIPCSDK.switchCamera()
    }


    /**
     * 开启或关闭扬声器播放
     * 该方法需要在加入频道后调用。
     * 如果用户使用了蓝牙耳机、有线耳机等外接音频播放设备，则该方法的设置无效，音频只会通过外接设备播放。当有多个外接设备时，音频会通过最后一个接入的设备播放。
     *
     * @param isOpen true:打开；false：关闭
     * @return 0: 方法调用成功;   < 0: 方法调用失败
     */
    fun setEnableSpeakerphone(isOpen: Boolean): Int {
        loged("setEnableSpeakerphone isOpen:$isOpen")
        val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
        audioManager.mode = AudioManager.MODE_IN_COMMUNICATION;
        audioManager.isSpeakerphoneOn = isOpen
        return RinoIPCSDK.setEnableSpeakerphone(isOpen)
    }

    fun testNotifyDeviceJoinLiveChannel(
        devId: String?,
        connectModel: Int,
        agoraUserTokenVO: AgoraUserTokenVO?,
    ) {
        RinoIPCBusiness.testNotifyDeviceJoinLiveChannel(
            devId,
            connectModel,
            agoraUserTokenVO,
            object : BaseRequestListener<Any?>() {
                override fun onResult(result: Any?) {
                    super.onResult(result)
                }

                override fun onError(error: String, msg: String) {
                    super.onError(error, msg)
                    LgUtils.w("$TAG testNotifyDeviceJoinLiveChannel     error=$error    msg=$msg")
                    ToastUtil.showMsg(msg)
                }
            })
    }

    /**
     * 根据sip协商后的音频编码格式推送流,当mimeType相同要根据速率clockRate判断
     */
    fun convertSip2AgoraAudioCodec(mimeType: String?, clockRate: Int?) {
        when (mimeType) {
            "PCMU" -> dynamicAudioCodec = 0
            "PCMA" -> dynamicAudioCodec = 8
            "G722" -> dynamicAudioCodec = 9
            "opus" -> dynamicAudioCodec = 120
            "speex" -> {
                when (clockRate) {
                    8000 -> {}
                    16000 -> {}
                    32000 -> {}
                }
            }

            "speex", "iLBC", "L16", "iSAC" -> dynamicAudioCodec = 0
        }
    }

    //App端暂时不需要推送视频
    fun convertSip2AgoraVideoCodec(mimeType: String) {
        when (mimeType) {
            "H264" -> {}
            "H265" -> {}
            "VP8" -> {}
        }
    }

    /*==================================音频通话业务======================================*/
    /**
     * 开始向某个频道推送音频流, 使用前请申请好麦克风权限并且已加入频道
     *
     * @param chanelId   需要推送的频道名,token 中的channelName
     * @param audioCodec 音频格式, 0: g711u 8k, 8: g711a 8k, 69: AAC 16k
     * @return 0: 方法调用成功;   < 0: 方法调用失败, -1 sdk未初始化
     */
    @Throws(java.lang.Exception::class)
    fun startPushAudioToChannel(localUid: Int, audioCodec: Int = dynamicAudioCodec): Int {
        loged("startPushAudioToChannel  localUid:$localUid dynamicAudioCodec:$dynamicAudioCodec")
        val chanelId = agoraUserTokenVO?.rtcToken?.channelName
        if (chanelId.isNullOrEmpty()) {
            return -2
        }
        val result = try {
            RinoIPCSDK.startPushAudioToChannel(chanelId, localUid, audioCodec)
        } catch (e: Exception) {
            Log.e("jake", "[jake] Rino Screen shot error  : $e ")
            -2
        }
        return result
    }

    /**
     * 停止向某个频道推送音频流
     *
     * @param chanelId 需要推送的频道名,token 中的channelName
     * @return 0: 方法调用成功;   < 0: 方法调用失败, -1 sdk未初始化
     */
    @Throws(java.lang.Exception::class)
    fun stopPushAudioToChannel(localUid: Int): Int {
        loged("stopPushAudioToChannel  localUid:$localUid")
        val chanelId = agoraUserTokenVO?.rtcToken?.channelName
        if (chanelId.isNullOrEmpty()) {
            return -2
        }

        val result = try {
            RinoIPCSDK.stopPushAudioToChannel(chanelId, localUid)
        } catch (e: Exception) {
            Log.e("jake", "[jake] Rino Screen shot error  : $e ")
            -2
        }
        return result
    }


    /*==================================视频通话业务======================================*/
    /**
     * 开始向某个频道推送视频流, 使用前请申请好麦克风权限并且已加入频道
     *
     * @param chanelId   需要推送的频道名,token 中的channelName
     * @param dimensions 推送的视频尺寸
     * @return 0: 方法调用成功;   < 0: 方法调用失败, -1 sdk未初始化
     */
    @Throws(java.lang.Exception::class)
    fun startPushVideoToChannel(
        chanelId: String?,
        localUid: Int,
        dimensions: VideoDimensions?,
    ): Int {
        return RinoIPCSDK.startPushVideoToChannel(chanelId, localUid, dimensions)
    }

    /**
     * 停止向某个频道推送视频流
     *
     * @param chanelId 需要推送的频道名,token 中的channelName
     * @return 0: 方法调用成功;   < 0: 方法调用失败, -1 sdk未初始化
     */
    @Throws(java.lang.Exception::class)
    fun stopPushVideoToChannel(chanelId: String?, uid: Int): Int {
        return RinoIPCSDK.stopPushVideoToChannel(chanelId, uid)
    }


    /**
     * 截取远端视频
     * 存储位置是否需要申请对应的权限
     *
     * @param chanelId      频道名
     * @param localUid      本地uuid RtcToken 中的uid
     * @param remoteUid     需要截取的远端推送视频流uid
     * @param filePath      文件路径  需精确到文件名及格式
     * @param saveToGallery 是否保存到相册, 需要提前申请好文件权限
     * @return 0: 方法调用成功;   < 0: 方法调用失败, -1 sdk未初始化
     * @description 调用后需要监听通知 [RinoIPCEventEmitter.RinoIPCEventTypeEnum.onSnapshotTaken] 获取拍照结果
     */
    @Throws(Exception::class)
    fun takeSnapshot(
        context: Context,
        displayName: String = "",
        saveToGallery: Boolean = false,
    ): Int {
//        val filePath = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!.absolutePath
        val chanelId = agoraUserTokenVO?.rtcToken?.channelName
        val localUidStr = agoraUserTokenVO?.userId
        val fileName = if (displayName.isNotEmpty()) {
            displayName + "@@@" + System.currentTimeMillis() + ".png"
        } else {
            localUidStr + "@@@" + System.currentTimeMillis() + ".png"
        }
        val filePath = getFileStoragePath(context, fileName).absolutePath
        val localUid = localUidStr?.toInt() ?: 0

        Log.e(
            "jake",
            "[jake] Rino截图 >> channelId = $chanelId ; userId = $localUid ; remoteUid = $remoteUid, filePath = $filePath"
        )
        if (chanelId.isNullOrEmpty()) {
            showToast(context.getString(R.string.take_screenshot_failed))
            return -1
        }
        val result = try {
            RinoIPCSDK.takeSnapshot(chanelId, localUid, remoteUid, filePath, saveToGallery)
        } catch (e: Exception) {
            Log.e("jake", "[jake] Rino Screen shot error  : $e ")
            -2
        }
        return result
    }

    fun takeTempSnapshot(context: Context, fileName: String): Int {
        val file = File(context.cacheDir, fileName)

        val chanelId = agoraUserTokenVO?.rtcToken?.channelName
        val localUidStr = agoraUserTokenVO?.userId
        val localUid = localUidStr?.toInt() ?: 0
        if (chanelId.isNullOrEmpty()) {
            showToast(context.getString(R.string.take_screenshot_failed))
            return -1
        }
        val result = try {
            RinoIPCSDK.takeSnapshot(chanelId, localUid, remoteUid, file.absolutePath, false)
        } catch (e: Exception) {
            Log.e("jake", "[jake] Rino Screen shot error  : $e ")
            -2
        }
        return result
    }

    fun getFileStoragePath(context: Context, fileName: String): File {
        val path = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        var file = File(path, fileName)

        var prefix = 1
        while (file.exists()) {
            file = File(path, prefix.toString() + "_" + fileName)
            Log.e("jake", "[jake] File with that name already exists, renamed to ${file.name}")
            prefix += 1
        }
        return file
    }

    fun getSnapshot(context: Context): List<String> {
        val fileList: ArrayList<String> = ArrayList()
        val file = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!
        val tempList: Array<File> = file.listFiles()
        for (i in tempList.indices) {
            if (tempList[i].isFile) {
                val fileName = "${file.path}/${tempList[i].name}"
                NLog.e("<jake> getAllScreenshot  ---->  $fileName")
                if (fileName.endsWith(".png")) {    //  根据自己的需要进行类型筛选
                    fileList.add(fileName)
                }
            }
        }
        Collections.sort(fileList, Comparator { o1, o2 ->
            try {
                val time1 = o1.substringAfter("@@@").substringBefore(".png").toLong()
                val time2 = o2.substringAfter("@@@").substringBefore(".png").toLong()
                time1.compareTo(time2)
            } catch (e: Exception) {
                e.printStackTrace()
                o1.compareTo(o2)
            }
        })
        NLog.e("<jake> getAllScreenshot  ${fileList.size}")
        return fileList

    }

    fun deleteSnapshot(filePath: String): Boolean {
        try {
            val fileLocal = File(filePath)
            if (fileLocal.exists()) {
                fileLocal.delete()
            }
            return true
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return false
    }

    /**
     * 刷新token, 需要确保uid不变
     *
     * @param channelName 频道名
     * @param localUid    本地uid
     * @param token       新token
     * @return 0：方法调用成功。< 0：方法调用失败。
     */
    private fun updateToken(channelName: String?, localUid: Int, token: String?): Int {
        return RinoIPCSDK.updateToken(channelName, localUid, token)
    }

    fun updateRinoToken() {
        val result = updateToken(
            getChannelName(),
            agoraUserTokenVO?.userId?.toInt() ?: 0,
            agoraUserTokenVO?.rtcToken?.rtcToken
        )
        NLog.e("[Rino] >> updateToken is $result")
        rinoRemotePlayer?.setToken(agoraUserTokenVO)
    }

    fun saveLog(message: String) {
        LogHandlerThread.instance?.writeLog(
            context,
            "RINO",
            "♥♥♥♥♥ $message ⌚⌚⌚${System.currentTimeMillis() - callStartBasicTimeStamp}"
        )
    }

    fun appendLineBreak() {
        LogHandlerThread.instance?.appendLineBreak(context)
    }
}